﻿#region License
/* ---------------------------------------------------------------- *
 *
 * This file is part of the Xcoordination Application Space
 * ("XcoAppSpace") http://www.xcoordination.com
 *
 * Copyright (C) 2009 Xcoordination http://www.xcoordination.com
 *
 * XcoAppSpace is free software; you can redistribute it and/or
 * modify it under the terms of version 2 of the GNU General
 * Public License as published by the Free Software Foundation.
 *
 * XcoAppSpace is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/
 * or write to the Free Software Foundation, Inc., 51 Franklin
 * Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * ---------------------------------------------------------------- */
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using XcoAppSpaces.Core.Ports;
#if !PocketPC && !SILVERLIGHT && !ANDROID && !NETFX_CORE
using ExpressionBuilder;
using System.Linq.Expressions;
#endif

namespace XcoAppSpaces.Core
{
	/// <summary>
	/// Non-generic base class for subscribing to events that are published by workers
	/// using the XcoPublisher class.
	/// </summary>
#if !NETFX_CORE
	[Serializable]
#endif
	public abstract class Subscribe
	{
		internal IPort Port { get; set; }
		internal Type PortType { get; set; }

		internal Subscribe(IPort port, Type portType)
		{
			this.Port = port;
			this.PortType = portType;
		}

		internal abstract bool MatchesFilter(object item);
	}

	/// <summary>
	/// Class for Subscribing to events that are published by workers
	/// using the XcoPublisher class.
	/// </summary>
	/// <typeparam name="T">The type of event.</typeparam>
#if !NETFX_CORE
	[Serializable]
#endif
	public class Subscribe<T> : Subscribe
	{
		/// <summary>
		/// Creates a new Subscribe message.
		/// </summary>
		public Subscribe() : base(null, typeof(T))
		{
		}

		/// <summary>
		/// Creates a new Subscribe message.
		/// </summary>
		/// <param name="eventPort">The port that should be subscribed to receive events.</param>
		public Subscribe(Port<T> eventPort)
			: base(eventPort, typeof(T))
		{}

        /// <summary>
        /// Creates a new Subscribe message.
        /// </summary>
        /// <param name="eventPort">The port that should be subscribed to receive events.</param>
		public Subscribe(IPort eventPort)
			: base(eventPort, typeof(T))
        { }

		/// <summary>
		/// The port that should be subscribed to to receive events.
		/// </summary>
		public Port<T> EventPort
		{
			get { return (Port<T>)base.Port; }
			set { base.Port = value; } //setter needed for Json serialization
		}

		#region Filter Expression Functionality
#if !PocketPC && !SILVERLIGHT && !ANDROID && !NETFX_CORE
		/// <summary>
		/// The expression that is used for filtering published messages.
		/// </summary>
		public EditableExpression FilterExpr { get; set; }

        [NonSerialized]
		private Predicate<T> filterFunc = null;

		/// <summary>
		/// Creates a new Subscribe message.
		/// </summary>
		/// <param name="eventPort">The port that should be subscribed to receive events.</param>
		/// <param name="filterExpr">Predicate that can be used for filtering the event messages.</param>
		public Subscribe(Port<T> eventPort, Expression<Predicate<T>> filterExpr)
			: base(eventPort, typeof(T))
		{
			this.FilterExpr = EditableExpression.CreateEditableExpression(filterExpr);
		}

        /// <summary>
        /// Creates a new Subscribe message.
        /// </summary>
        /// <param name="eventPort">The port that should be subscribed to receive events.</param>
        /// <param name="filterExpr">Predicate that can be used for filtering the event messages.</param>
		public Subscribe(IPort eventPort, Expression<Predicate<T>> filterExpr)
            : base(eventPort, typeof(T))
        {
            this.FilterExpr = EditableExpression.CreateEditableExpression(filterExpr);
        }
        
        internal override bool MatchesFilter(object item)
		{
			if (FilterExpr == null)
			{
				return item is T;
			}
			if (filterFunc == null)
			{
				var exp = (Expression<Predicate<T>>)FilterExpr.ToExpression();
				filterFunc = exp.Compile();
			}
			return item is T && filterFunc((T)item);
		}
#else
		internal override bool MatchesFilter(object item)
		{
			return item is T;
		}
#endif
		#endregion
	}
}
